package com.market.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.market.entity.ChatList;
import com.market.entity.ChatMessage;
import com.market.entity.User;
import com.market.mapper.ChatListMapper;
import com.market.model.dto.ChatListCreateDto;
import com.market.model.vo.ChatListVo;
import com.market.service.ChatListService;
import com.market.service.ChatMessageService;
import com.market.service.UserService;
import com.market.constants.ResultCode;
import com.market.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Service
@Slf4j
public class ChatListServicelmpl extends ServiceImpl<ChatListMapper, ChatList> implements ChatListService {
  @Autowired
  private UserService userService;
  @Autowired
  private StringRedisTemplate redisTemplate;
  @Autowired
  private ChatMessageService chatMessageService;
  
  /**
   * 创建聊天会话
   * 如果用户之间已存在关于该商品的聊天，则返回已有会话ID
   * 否则创建新的聊天会话并发送初始消息
   * @param dto 聊天会话创建参数，包含商品信息和目标用户ID
   * @return 聊天会话ID
   */
  @Transactional(rollbackFor = Exception.class)
  public String create(ChatListCreateDto dto) {
    // 设置发送方用户ID为当前登录用户
    dto.setFromUserId(StpUtil.getLoginIdAsString());
    
    // 查询是否已存在相同用户间关于该商品的聊天会话
    // 查询条件：相同商品ID且相同的用户组合(不分发送和接收方向)
    ChatList one = lambdaQuery().eq(ChatList::getProductId, dto.getProductId())
      .and(wrapper -> {
        wrapper.eq(ChatList::getFromUserId, dto.getFromUserId()).eq(ChatList::getToUserId, dto.getToUserId())
          .or().eq(ChatList::getFromUserId, dto.getToUserId()).eq(ChatList::getToUserId, dto.getFromUserId());
      })
      .one();
      
    // 如果已存在聊天会话，直接返回该会话ID
    if (!BeanUtil.isEmpty(one)) {
      return one.getId();
    }
    
    // 创建新的聊天会话对象
    ChatList chatList = new ChatList();
    // 设置发送方用户ID
    chatList.setFromUserId(dto.getFromUserId());
    // 设置接收方用户ID
    chatList.setToUserId(dto.getToUserId());
    
    // 查询并设置发送方用户信息
    User fromUser = userService.getById(dto.getFromUserId());
    chatList.setFromUserNick(fromUser.getNickName());
    chatList.setFromUserAvatar(fromUser.getAvatar());
    
    // 查询并设置接收方用户信息
    User toUser = userService.getById(dto.getToUserId());
    chatList.setToUserNick(toUser.getNickName());
    chatList.setToUserAvatar(toUser.getAvatar());
    
    // 设置相关商品信息
    chatList.setProductId(dto.getProductId());
    chatList.setProductImage(dto.getProductImage());
    
    // 设置创建和更新时间
    chatList.setCreateTime(new Date().getTime());
    chatList.setUpdateTime(new Date().getTime());
    
    // 保存聊天会话信息到数据库
    boolean save = save(chatList);
    // 如果保存失败，抛出服务异常
    if (!save) throw new ServiceException(ResultCode.SaveError);
    
    // 创建初始聊天消息对象
    ChatMessage chatMessage = new ChatMessage();
    // 设置所属的聊天会话ID
    chatMessage.setChatListId(chatList.getId());
    // 设置初始消息内容
    chatMessage.setContent("我刚刚查看了您的宝贝哦～");
    // 设置发送方用户信息
    chatMessage.setFromUserId(StpUtil.getLoginIdAsString());
    chatMessage.setFromUserNick(fromUser.getNickName());
    // 设置接收方用户信息
    chatMessage.setToUserId(dto.getToUserId());
    chatMessage.setToUserNick(toUser.getNickName());
    // 设置消息为未读状态
    chatMessage.setIsRead(0);
    // 设置发送时间
    chatMessage.setSendTime(new Date().getTime());
    
    // 保存聊天消息到数据库
    boolean save1 = chatMessageService.save(chatMessage);
    // 如果保存失败，抛出服务异常
    if (!save1) throw new ServiceException(ResultCode.SaveError);
    
    // 返回创建的聊天会话ID
    return chatList.getId();
  }
  
  /**
   * 获取当前用户的聊天会话列表
   * 包括用户作为发送方和接收方的所有会话
   * 按更新时间降序排序，并附带最后一条消息和未读消息数
   * @return 当前用户的聊天会话列表
   */
  @Override
  public List<ChatListVo> getMyChatList() {
    // 获取当前登录用户ID
    String userId = StpUtil.getLoginIdAsString();
    // 创建结果集合用于存储聊天会话
    ArrayList<ChatListVo> chatListVos = new ArrayList<>();
    
    // 查询当前用户参与的所有聊天会话（作为发送方或接收方）
    // 按更新时间降序排序，最新的会话排在前面
    List<ChatList> chatLists = lambdaQuery()
        .eq(ChatList::getFromUserId, userId)
        .or()
        .eq(ChatList::getToUserId, userId)
        .orderByDesc(ChatList::getUpdateTime)
        .list();
    
    // 遍历每个聊天会话，添加最后一条消息和未读消息数
    for (ChatList chatList : chatLists) {
      // 获取并添加最后一条消息信息
      ChatListVo vo = _getLastMessage(chatList);
      // 获取并设置该会话的未读消息数量
      Integer noReadCount = _getNoReadCount(chatList.getId());
      vo.setNoReadCount(noReadCount);
      // 将完整的会话信息添加到结果集合
      chatListVos.add(vo);
    }
    
    // 返回聊天会话列表
    return chatListVos;
  }
  
  @Override
  public int getNoReadTotal() {
    int noReadTotal = 0;
    String userId = StpUtil.getLoginIdAsString();
    List<ChatList> chatLists = lambdaQuery().eq(ChatList::getFromUserId, userId).or().eq(ChatList::getToUserId, userId)
      .orderByDesc(ChatList::getUpdateTime).list();
    for (ChatList chatList : chatLists) {
      int noReadCount = _getNoReadCount(chatList.getId());
      noReadTotal += noReadCount;
    }
    return noReadTotal;
  }
  
  /**
   * 获取指定聊天会话的未读消息数量
   * 合并Redis缓存和数据库中的未读消息计数
   * @param chatListId 聊天会话ID
   * @return 未读消息总数
   */
  private int _getNoReadCount(String chatListId) {
    // 初始化未读消息计数
    int noRead = 0;
    // 获取Redis列表操作对象
    ListOperations<String, String> operations = redisTemplate.opsForList();
    // 从Redis获取指定聊天会话的所有消息
    List<String> messageList = operations.range("chat:message:" + chatListId, 0, -1);
    
    // 遍历Redis中的消息，统计发送给当前用户且未读的消息数量
    for (String redisMessage : messageList) {
      // 解析消息JSON对象
      JSONObject jsonObject = JSON.parseObject(redisMessage);
      // 判断是否为未读消息（isRead=0）且接收者是当前用户
      if ("0".equals(jsonObject.getString("isRead")) && 
          jsonObject.getString("toUserId").equals(StpUtil.getLoginIdAsString())) {
        // 未读计数加1
        noRead += 1;
      }
    }
    
    // 从数据库中查询发送给当前用户且未读的消息列表
    List<ChatMessage> list = chatMessageService.lambdaQuery()
      .eq(ChatMessage::getChatListId, chatListId)
      .eq(ChatMessage::getToUserId, StpUtil.getLoginIdAsString())
      .eq(ChatMessage::getIsRead, 0).list();
    
    // 将数据库中的未读消息数量添加到计数中
    noRead += list.size();
    
    // 返回总的未读消息数量
    return noRead;
  }
  
  /**
   * 获取聊天会话的最后一条消息
   * 优先从Redis缓存获取，如果没有则从数据库获取
   * @param chatList 聊天会话对象
   * @return 包含最后一条消息的聊天会话视图对象
   */
  private ChatListVo _getLastMessage(ChatList chatList) {
    // 将聊天会话对象转换为视图对象
    ChatListVo vo = BeanUtil.toBean(chatList, ChatListVo.class);
    // 获取Redis列表操作对象
    ListOperations<String, String> operations = redisTemplate.opsForList();
    // 从Redis获取指定聊天会话的所有消息
    List<String> messageList = operations.range("chat:message:" + chatList.getId(), 0, -1);
    
    // 如果Redis中存在消息
    if (!messageList.isEmpty()) {
      // 获取最后一条消息（列表中的最后一个元素）
      String lastMessage = redisTemplate.opsForList().index("chat:message:" + chatList.getId(), messageList.size() - 1);
      // 将消息字符串转换为ChatMessage对象
      ChatMessage chatMessage = _convertMessage(lastMessage);
      // 设置最后一条消息到视图对象中
      vo.setChatMessage(chatMessage);
    } else {
      // 如果Redis中没有消息，则从数据库获取最后一条消息
      ChatMessage message = chatMessageService.lambdaQuery()
          .eq(ChatMessage::getChatListId, chatList.getId())
          .orderByDesc(ChatMessage::getSendTime)
          .last("limit 0,1").one();
      
      // 如果数据库中也没有消息，则设置一个空的消息对象
      if (BeanUtil.isEmpty(message)) {
        vo.setChatMessage(new ChatMessage());
      } else {
        // 设置从数据库获取的最后一条消息到视图对象中
        vo.setChatMessage(message);
      }
    }
    
    // 返回包含最后一条消息的聊天会话视图对象
    return vo;
  }
  
  /**
   * 将消息JSON字符串转换为ChatMessage对象
   * 解析JSON格式的消息并填充到实体对象中
   * @param message JSON格式的消息字符串
   * @return 转换后的ChatMessage对象
   */
  private ChatMessage _convertMessage(String message) {
    // 创建新的ChatMessage对象
    ChatMessage chatMessage = new ChatMessage();
    // 将消息字符串解析为JSON对象
    JSONObject jsonObject = JSON.parseObject(message);
    
    // 从JSON对象中提取各字段值，并设置到ChatMessage对象中
    // 设置聊天会话ID
    chatMessage.setChatListId(jsonObject.getString("chatListId"));
    // 设置消息内容
    chatMessage.setContent(jsonObject.getString("content"));
    // 设置发送者用户ID
    chatMessage.setFromUserId(jsonObject.getString("fromUserId"));
    // 设置接收者用户ID
    chatMessage.setToUserId(jsonObject.getString("toUserId"));
    // 设置消息是否已读状态（0未读，1已读），并转换为整数类型
    chatMessage.setIsRead(Integer.valueOf(jsonObject.getString("isRead")));
    // 设置消息发送时间戳，并转换为长整型
    chatMessage.setSendTime(Long.valueOf(jsonObject.getString("sendTime")));
    
    // 返回填充好数据的ChatMessage对象
    return chatMessage;
  }
}
